package com.hqd.ch03.v25.aop.framework;

import com.hqd.ch03.v25.aop.*;
import com.hqd.ch03.v25.aop.support.DefaultIntroductionAdvisor;
import com.hqd.ch03.v25.aop.support.DefaultPointcutAdvisor;
import com.hqd.ch03.v25.aop.target.EmptyTargetSource;
import com.hqd.ch03.v25.aop.target.SingletonTargetSource;
import com.hqd.ch03.v25.aopalliance.aop.Advice;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * advice管理类
 */
public class AdvisedSupport extends ProxyConfig implements Advised {
    public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
    AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
    TargetSource targetSource = EMPTY_TARGET_SOURCE;
    private List<Advisor> advisors = new ArrayList<>();
    private List<Class<?>> interfaces = new ArrayList<>();

    public AdvisedSupport(Object target) {
        setTarget(target);
    }

    public AdvisedSupport() {
    }

    public Object getTarget() {
        return getTargetSource().getTarget();
    }

    public void setTarget(Object target) {
        setTargetSource(new SingletonTargetSource(target));
    }

    @Override
    public Class<?> getTargetClass() {
        return this.targetSource.getTargetClass();
    }

    @Override
    public Class<?>[] getProxiedInterfaces() {
        return interfaces.toArray(new Class[0]);
    }

    @Override
    public boolean isInterfaceProxied(Class<?> intf) {
        for (Class<?> proxyIntf : this.interfaces) {
            if (intf.isAssignableFrom(proxyIntf)) {
                return true;
            }
        }
        return false;
    }

    public void addAdvisors(Collection<Advisor> advisors) {
        if (CollectionUtils.isNotEmpty(advisors)) {
            this.advisors.addAll(advisors);
        }
    }

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        return this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
    }

    public Advisor[] getAdvisors() {
        return advisors.toArray(new Advisor[]{});
    }

    @Override
    public int getAdvisorCount() {
        return Advised.super.getAdvisorCount();
    }

    @Override
    public void addAdvisor(Advisor advisor) {
        if (advisor != null) {
            this.advisors.add(advisor);
        }
    }

    @Override
    public void addAdvisor(int pos, Advisor advisor) {
        this.advisors.add(pos, advisor);
    }

    @Override
    public boolean removeAdvisor(Advisor advisor) {
        return this.advisors.remove(advisor);
    }

    @Override
    public void removeAdvisor(int index) {
        this.advisors.remove(index);
    }

    @Override
    public int indexOf(Advisor advisor) {
        return this.advisors.indexOf(advisor);
    }

    @Override
    public boolean replaceAdvisor(Advisor a, Advisor b) {
        int index = indexOf(a);
        if (index == -1) {
            return false;
        }
        removeAdvisor(index);
        addAdvisor(index, b);
        return true;
    }

    @Override
    public void addAdvice(Advice advice) {
        addAdvice(this.advisors.size(), advice);
    }

    @Override
    public void addAdvice(int pos, Advice advice) {
        /**
         * 根据通知类型，创建对于的Advisor
         */
        if (advice instanceof IntroductionInfo) {
            addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
        } else if (advice instanceof DynamicIntroductionAdvice) {
            throw new RuntimeException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
        } else {
            addAdvisor(pos, new DefaultPointcutAdvisor(advice));
        }
    }

    @Override
    public boolean removeAdvice(Advice advice) {
        int index = indexOf(advice);
        if (index == -1) {
            return false;
        } else {
            removeAdvisor(index);
            return true;
        }
    }

    @Override
    public int indexOf(Advice advice) {
        for (int i = 0; i < this.advisors.size(); i++) {
            Advisor advisor = this.advisors.get(i);
            if (advisor.getAdvice() == advice) {
                return i;
            }
        }
        return -1;
    }

    public List<Class<?>> getInterfaces() {
        return interfaces;
    }

    public void setInterfaces(Class<?>... interfaces) {
        this.interfaces.clear();
        for (Class<?> ifc : interfaces) {
            addInterface(ifc);
        }
    }

    public void addInterfaces(Class<?>... classes) {
        this.interfaces.addAll(Arrays.asList(classes));
    }


    public void addInterface(Class<?> itf) {
        this.interfaces.add(itf);
    }

    public TargetSource getTargetSource() {
        return targetSource;
    }

    @Override
    public void setTargetSource(TargetSource targetSource) {
        this.targetSource = (targetSource != null ? targetSource : EMPTY_TARGET_SOURCE);
    }

    AdvisedSupport getConfigurationOnlyCopy() {
        AdvisedSupport copy = new AdvisedSupport();
        copy.copyFrom(this);
        copy.targetSource = this.targetSource;
        copy.advisorChainFactory = this.advisorChainFactory;
        copy.interfaces = new ArrayList<>(this.interfaces);
        copy.advisors = new ArrayList<>(this.advisors);
        return copy;
    }
}
